package yartiss.engine.simulation.scheduling.policies.fp;

import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;

import yartiss.engine.simulation.Event;
import yartiss.engine.simulation.IEnergyProfile;
import yartiss.engine.simulation.ISchedulingPolicy;
import yartiss.engine.simulation.events.EventGenerator;
import yartiss.engine.simulation.scheduling.policies.AbstractMonoProcessorSchedulingPolicy;
import yartiss.engine.simulation.scheduling.policies.tools.FPSlackEnergyManager;
import yartiss.engine.simulation.scheduling.policies.tools.FPSlackTimeManager;
import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.ITaskSet;
import yartiss.engine.simulation.system.taskset.TaskSetFactory;

public class EHPFP7SchedulingPolicy extends AbstractMonoProcessorSchedulingPolicy {

	private final FPSlackTimeManager slackTimeManager;
	private final FPSlackEnergyManager slackEnergyManager;

	public EHPFP7SchedulingPolicy(FPSlackTimeManager slackTimeManager, FPSlackEnergyManager slackEnergyManager) {
		this.slackTimeManager = slackTimeManager;
		this.slackEnergyManager = slackEnergyManager;
	}

	@Override
	public String getPolicyName() {
		return "EH_PFP_7";
	}

	@Override
	public ITaskSet createTaskSet() {
		return TaskSetFactory.getNewInstance("priority");
	}



	//	private long wakeup = -1;
	//	private long wakeupDate = 0;
	private Map<String, Long> metrics = new HashMap<String, Long>();

	long sleepUntil = -1;
	long sleepUntilDecision = 0;


	@Override
	public ITask chooseNextTask(ITaskSet taskSet, IEnergyProfile energyProfile, long date, EventGenerator evGen) {
		metrics.clear();




		if (date == sleepUntilDecision)
			sleepUntil = -1;

		SortedSet<ITask> activeTasks = taskSet.getActiveTasks(date);

		if (!activeTasks.isEmpty()) {
			ITask first = activeTasks.first();
			if (date < sleepUntil) {// on doit laisser le systeme se recharger

				// Au cas ou le st a change (devrait pas arriver)
				long st = Math.max(0, slackTimeManager.getSlackTime(date, taskSet));
				metrics.put(Event.SlackTime, st);
				if(st<=0){
					sleepUntilDecision=date;
					evGen.generateEvent("check_energy_state", activeTasks.first(), date, null);
				}
				// fin du au cas ou

				return null;
			}
			if (date == sleepUntil) {// on doit recalculer le slack time,
				// modifier sleepUntil et recommencer

				long de = energyProfile.getMaxEnergyLevel() - energyProfile.getCurrentEnergyLevel();
				if (de < energyProfile.getPowerRate()) {// on reprend
					// l'execution
					sleepUntilDecision = date;
					sleepUntil = -1; // inutile mais pour clarifier le code
					evGen.generateEvent("check_energy_state", activeTasks.first(), date, null);
					return null;
				}
				long st = Math.max(0, slackTimeManager.getSlackTime(date, taskSet));
				metrics.put(Event.SlackTime, st);
				// if(st>0){
				long dt = Math.min(de / energyProfile.getPowerRate(), st);
				evGen.generateEvent("check_energy_state", activeTasks.first(), date + dt, null);
				sleepUntilDecision = date;
				sleepUntil = date + dt;
				return null;
				// } La suite est commente car revient au meme
				// sleepUntilDecision=date;
				// sleepUntil=-1; // inutile mais pour clarifier le code
				// evGen.generateEvent("check_energy_state",
				// activeTasks.first(),date, null);
				// return null;
			}
			if (date > sleepUntil) {// on reprend l'execution si c'est possible

				long se = energyProfile.getCurrentEnergyLevel() > energyProfile.getMinEnergyLevel() ? slackEnergyManager.getSlackEnergy(date, taskSet, energyProfile) : -1;
				metrics.put(Event.SlackEnergy, se);

				long hlcet = energyProfile.howLongCanExecute(se,first);
				if (hlcet > 0) {
					if (hlcet < activeTasks.first().getRemainingCost())
						evGen.generateEvent("check_energy_state", activeTasks.first(), date + hlcet, null);
					return activeTasks.first();
				}
				long de = energyProfile.getMaxEnergyLevel() - energyProfile.getCurrentEnergyLevel();

				long st = Math.max(0, slackTimeManager.getSlackTime(date, taskSet));
				metrics.put(Event.SlackTime, st);

				if(st<=0){// pour que la tache depasse son echeance 
					evGen.generateEvent("check_energy_state", activeTasks.first(), date + 1, null);
					return null;
				}

				long dt = Math.min(Math.max(1,de / energyProfile.getPowerRate()), st);
				evGen.generateEvent("check_energy_state", activeTasks.first(), date + dt, null);
				sleepUntilDecision = date;
				sleepUntil = date + dt;
				return null;

			}

		}
		return null;
	}

	@Override
	public Map<String, Long> getMetrics() {
		return new HashMap<String, Long>(metrics);
	}

	@Override
	public ISchedulingPolicy newInstance() {
		return new EHPFP7SchedulingPolicy(new FPSlackTimeManager(), new FPSlackEnergyManager());
	}

}
